unit ORM.Base.ObjectDBX;

interface

uses
  System.Rtti, Data.DBXCommon, ORM.Types;

type
  TObjectDBX = class
  strict private
    class var FDBXCommand : TDBXCommand;
  private
    class function GetValueFromDBXReader( const pValue : TDBXValue; const pValueType : TDBXValueType) : TValue; static;
    class function SetValueInObject( const pProperty : TRttiProperty; const pValue : TValue;
                                     pObject : TObject; pFieldName : string) : Boolean; static;
    class procedure GetValueIsEnumeration( const pProperty: TRttiProperty; pObject: TObject; const pValue : TValue); static;
    class procedure GetValueIsClass( const pProperty: TRttiProperty; pObject: TObject; const pValue : TValue); static;
    class procedure GetValueIsRecord( const pProperty: TRttiProperty; pObject: TObject; const pValue : TValue); static;
  public
    class function GetDBXReader( const pSQLCommand : string) : TDBXReader; overload;
    class function GetDBXReader( const pObject : TObject ): TDBXReader; overload;
    class function GetDBXReader( const pObject : TOBJect; const pFilter : string) : TDBXReader; overload;
    class function GetDBXReader( const pObject : TObject; const pFilter : string; const pOrdination : string) : TDBXReader; overload;
    class function ConsultForAnyField( const pObjeto : TObject) : TDBXReader;

    class function LoadObjectFomDBXReader<T : class>(const pReader: TDBXReader): T;
    class function LoadObjectListFromDBXReder<T : class>(const pReader : TDBXReader) : TObjectList<T>;
    class procedure DealocateCommand;
  end;

implementation

uses
  ORM.SQLCommands, System.SysUtils, SqlTimSt, jpeg, ORM.Drive.Connection,
  Classes, ORM.Attributes, System.TypInfo, ORM.Base, Data.FMTBcd,
  ORM.Types.Base;

{ TObjectDBX }

class function TObjectDBX.GetDBXReader(const pObject: TObject): TDBXReader;
begin
  try
    FDBXCommand := TConnectionDBX.GetConnection.DBXConnection.CreateCommand;
    FDBXCommand.Text := TSQLCommands.StringConsult(pObject, '', '');
    FDBXCommand.Prepare;
    Result := FDBXCommand.ExecuteQuery;
  except
    result := nil;
  end;
end;

class function TObjectDBX.GetDBXReader(const pObject: TObject; const pFilter: string): TDBXReader;
begin
  try
    FDBXCommand := TConnectionDBX.GetConnection.DBXConnection.CreateCommand;
    FDBXCommand.Text := TSQLCommands.StringConsult(pObject, pFilter, '');
    FDBXCommand.Prepare;
    Result := FDBXCommand.ExecuteQuery;
  except
    result := nil;
  end;
end;

class function TObjectDBX.ConsultForAnyField(const pObjeto: TObject): TDBXReader;
begin
  try
    FDBXCommand := TConnectionDBX.GetConnection.DBXConnection.CreateCommand;
    FDBXCommand.Text := TSQLCommands.StringConsult(pObjeto);
    FDBXCommand.Prepare;
    result :=  FDBXCommand.ExecuteQuery;;
  except
    result := nil;
  end;
end;

class procedure TObjectDBX.DealocateCommand;
begin
  if Assigned(FDBXCommand) then
    FreeAndNil(FDBXCommand);
end;

class function TObjectDBX.GetDBXReader(const pObject: TObject; const pFilter, pOrdination: string): TDBXReader;
begin
  try
    FDBXCommand := TConnectionDBX.GetConnection.DBXConnection.CreateCommand;
    FDBXCommand.Text := TSQLCommands.StringConsult(pObject, pFilter, pOrdination);
    FDBXCommand.Prepare;
    Result := FDBXCommand.ExecuteQuery;
  except
    Result := nil;
  end;
end;

class function TObjectDBX.GetDBXReader(const pSQLCommand: string): TDBXReader;
begin
  try
    FDBXCommand := TConnectionDBX.GetConnection.DBXConnection.CreateCommand;
    FDBXCommand.Text := pSQLCommand;
    FDBXCommand.Prepare;
    Result := FDBXCommand.ExecuteQuery;
  except
    Result := nil;
  end;
end;

class function TObjectDBX.GetValueFromDBXReader( const pValue : TDBXValue; const pValueType : TDBXValueType) : TValue;
begin
  case pValueType.DataType of
    TDBXDataTypes.UnknownType : Result := TValue.Empty;
    TDBXDataTypes.AnsiStringType: Result := StringReplace(string(pValue.GetAnsiString), '"','',[rfReplaceAll]);
    TDBXDataTypes.DateType : Result := pValue.GetDate;
    TDBXDataTypes.BlobType :
    begin
      if pValueType.SubType = 22 then
        Result := StringReplace(pValue.AsString,'"','',[rfReplaceAll])
      else
        Result := pValue.GetStream(True);
    end;
    TDBXDataTypes.BooleanType: Result := pValue.AsBoolean;
    TDBXDataTypes.Int16Type: Result := pValue.GetInt16;
    TDBXDataTypes.Int32Type: Result := pValue.GetInt32;
    TDBXDataTypes.DoubleType: Result :=  pValue.GetDouble;
    TDBXDataTypes.BcdType : Result := BcdToDouble(pValue.GetBcd);
    TDBXDataTypes.BytesType : Result := TValue.Empty;
    TDBXDataTypes.TimeType : Result := pValue.GetTime;
    TDBXDataTypes.DateTimeType : Result := pValue.AsDateTime;
    TDBXDataTypes.UInt16Type : Result := pValue.GetUInt16;
    TDBXDataTypes.Uint32Type : Result := pValue.ASInt32;
    TDBXDataTypes.VarBytesType : Result := TValue.Empty;
    TDBXDataTypes.CursorType : Result := TValue.Empty;
    TDBXDataTypes.Int64Type: Result := pValue.GetInt64;
    TDBXDataTypes.Uint64Type: Result := pValue.GetInt64;
    TDBXDataTypes.AdtType : Result := TValue.Empty;
    TDBXDataTypes.ArrayType : Result := TValue.Empty;
    TDBXDataTypes.RefType : Result := TValue.Empty;
    TDBXDataTypes.TableType: Result := TValue.Empty;
    TDBXDataTypes.TimeStampType: Result := SQLTimeStampToDateTime(pValue.AsTimeStamp);
    TDBXDataTypes.CurrencyType: Result := pValue.GetDouble;
    TDBXDataTypes.WideStringType: Result := StringReplace(pValue.GetWideString,'"','',[rfReplaceAll]);
    TDBXDataTypes.SingleType: Result := pValue.AsSingle;
    TDBXDataTypes.Int8Type: Result := pValue.GetInt8;
    TDBXDataTypes.Uint8Type: Result := pValue.GetUInt8;
    TDBXDataTypes.ObjectType: Result := TValue.Empty;
    TDBXDataTypes.CharArrayType : Result := TValue.Empty;
    TDBXDataTypes.IntervalType : Result := TValue.Empty;
    TDBXDataTypes.BinaryBlobType : Result := TValue.Empty;
    TDBXDataTypes.DBXConnectionType : Result := TValue.Empty;
    TDBXDataTypes.VariantType : Result := TValue.FromVariant(pValue.AsVariant);
    TDBXDataTypes.TimeStampOffsetType : Result := TValue.Empty;
    TDBXDataTypes.JsonValueType : Result := TValue.Empty;
    TDBXDataTypes.CallbackType : Result := TValue.Empty;
    TDBXDataTypes.MaxBaseTypes: Result := TValue.Empty;
  end;
end;

class procedure TObjectDBX.GetValueIsRecord(const pProperty : TRttiProperty; pObject : TObject; const pValue : TValue);
var
  FRecord : TRttiRecordType;
  FField : TRttiField;
  FFieldRecord : TRttiField;
  FContext : TRttiContext;
begin
  try
    for FField in FContext.GetType(pObject.ClassType).GetFields do
    begin
      if (FField.Name =  'F' + pProperty.Name) then
      begin
        FRecord := pProperty.PropertyType.AsRecord;
        for FFieldRecord in FRecord.GetFields do
        begin
          if FFieldRecord.Name = 'FValue' then
            FFieldRecord.SetValue(PByte(pObject) + FField.Offset, pValue)
          else
            FFieldRecord.SetValue(PByte(pObject) + FField.Offset, TValue(CValorReferencia));
        end;
        Break;
      end;
    end;
  finally
    FContext.Free;
  end;
end;

class procedure TObjectDBX.GetValueIsEnumeration(const pProperty : TRttiProperty; pObject : TObject;
      const pValue : TValue);
var
  NewValue : TValue;
begin
  if pProperty.PropertyType.Name <> 'booleam' then
  begin
    NewValue := pProperty.GetValue(pObject);
    NewValue := TValue.FromOrdinal(NewValue.TypeInfo, pValue.AsOrdinal);
    pProperty.SetValue(pObject,  NewValue);
  end;
end;

class procedure TObjectDBX.GetValueIsClass(const pProperty : TRttiProperty; pObject : TObject;
      const pValue : TValue);
var
  FJpeg : TJPEGImage;
  NameTypeField : string;
begin
  NameTypeField := LowerCase(pProperty.PropertyType.Name);
  if NameTypeField = 'tjpegimage' then
  begin
    FJpeg := TJPEGImage.Create;
    FJpeg.LoadFromStream(TStream(pValue.AsObject));
    pProperty.SetValue(pObject,FJpeg);
  end;
  FreeAndNil(FJpeg);
end;

class function TObjectDBX.SetValueInObject(const pProperty : TRttiProperty; const pValue : TValue;
                                               pObject : TObject; pFieldName : string) : Boolean;
var
  FAtributte : TCustomAttribute;
begin
  Result := False;
  try
    for FAtributte in pProperty.GetAttributes do
    begin
      if FAtributte is TColumn then
      begin
        if (FAtributte as TColumn).Name = pFieldName then
        begin
          if not pValue.IsEmpty then
          begin
            case pProperty.PropertyType.TypeKind of
              tkEnumeration : GetValueIsEnumeration(pProperty, pObject, pValue);
              tkClass : GetValueIsClass(pProperty, pObject, pValue);
              tkRecord : TObjectDBX.GetValueIsRecord(pProperty, pObject, pValue);
            else
              pProperty.SetValue(pObject, pValue);
            end
          end;
          Result := True;
          Break;
        end;
      end
      else
      if FAtributte is TPrimaryKey then
      begin
        if (FAtributte as TPrimaryKey).Name = pFieldName then
        begin
          if not pValue.IsEmpty then
          begin
            if not pProperty.PropertyType.IsRecord then
              pProperty.SetValue(pObject, pValue.AsInteger)
            else
              TObjectDBX.GetValueIsRecord(pProperty, pObject, pValue);
          end;
          Result := True;
          Break;
        end;
      end;
    end;
  except
    raise Exception.Create('Error ao Atribuir Valor (Enumerador, TJpegImage ou Record');
  end;
end;

class function TObjectDBX.LoadObjectFomDBXReader<T>(const pReader: TDBXReader): T;
var
  FObject: T;
  I : Integer;
  FContext: TRttiContext;
  FProperty: TRttiProperty;
  FValue : TValue;
  FieldName : string;
begin
  FObject := TGeneric<T>.CreateObject;
  try
    for I := 0 to pReader.ColumnCount - 1 do
    begin
      FieldName := UpperCase(pReader.ValueType[I].Name);
      if pReader.Value[I].IsNull then
        Continue;

      FValue := GetValueFromDBXReader(pReader.Value[I], pReader.ValueType[I]);

      for FProperty in FContext.GetType(TObject(FObject).ClassType).GetProperties do
      begin
        if SetValueInObject(FProperty, FValue, FObject, FieldName) then
          Break;
      end;
    end;
  finally
    Result := FObject;
    FreeAndNil(FProperty);
    FContext.Free;
  end;
end;

class function TObjectDBX.LoadObjectListFromDBXReder<T>(const pReader: TDBXReader): TObjectList<T>;
var
  FObject : T;
begin
  Result := TObjectList<T>.Create;
  while pReader.Next do
  begin
    FObject := LoadObjectFomDBXReader<T>(pReader);
    Result.Add(FObject);
  end;
end;

end.
